package knowledgeGraph

import (
	"dustbe/database"
	"net/http"
	"strconv"
	"strings"

	"github.com/gin-gonic/gin"
	"github.com/neo4j/neo4j-go-driver/v5/neo4j"
)

type EchartsNode struct {
	Name       string `json:"name"`
	Desc       string `json:"desc"`
	SymbolSize int    `json:"symbolSize"`
	Category   int    `json:"category"`
}

type EchartsLink struct {
	Source string `json:"source"`
	Target string `json:"target"`
	Name   string `json:"name"`
}

type EchartsReturn struct {
	Data  []EchartsNode `json:"data"`
	Links []EchartsLink `json:"links"`
}

var NodeIdMapping = map[string]int{
	"query":   0,
	"event":   1,
	"country": 2,
	"group":   3,
	"person":  4,
	"place":   5,
	"time":    6,
}

func makeEchartNode(nodePtr *neo4j.Node) *EchartsNode {
	name := nodePtr.Props["name"].(string)
	desc, ok := nodePtr.Props["desc"]
	var descString string
	if !ok {
		descString = ""
	} else {
		descString = desc.(string)
	}

	var natureLabel string = ""
	for _, label := range nodePtr.Labels {
		label = strings.ToLower(label)
		if len(natureLabel) == 0 {
			natureLabel = label
		}
		if label != "event" {
			natureLabel = label
		}
	}

	category, ok := NodeIdMapping[natureLabel]
	if !ok {
		category = 0
	}

	echartNode := EchartsNode{
		Name:       name,
		Desc:       descString,
		SymbolSize: 40,
		Category:   category,
	}

	return &echartNode
}

func makeEchartLink(relationshipPtr *neo4j.Relationship, id2nodePtr map[int64]*neo4j.Node) *EchartsLink {
	startNodePtr := id2nodePtr[relationshipPtr.StartId]
	endNodePtr := id2nodePtr[relationshipPtr.EndId]
	source := startNodePtr.Props["name"].(string)
	target := endNodePtr.Props["name"].(string)

	echartLink := EchartsLink{
		Name:   relationshipPtr.Type,
		Source: source,
		Target: target,
	}
	return &echartLink
}

func removeDuplicateNodeName(duplicateNodeNameToLeftTime map[string]int, node *neo4j.Node) {
	name := node.Props["name"].(string)
	_, ok := duplicateNodeNameToLeftTime[name]
	if !ok {
		duplicateNodeNameToLeftTime[name] = 0
	} else {
		// 出现了重复 node
		duplicateTime := duplicateNodeNameToLeftTime[name]
		new_name := name + "-" + strconv.Itoa(duplicateTime)
		node.Props["name"] = new_name
		duplicateNodeNameToLeftTime[name]++
	}
}

func makeEchartReturn(tuples []database.Neo4jTuple) *EchartsReturn {

	id2nodePtr := make(map[int64]*neo4j.Node)

	// 去重用的，其中int代表这个词出现的次数
	duplicateNodeNameToLeftTime := make(map[string]int, 0)

	// 将 tuples 的格式改写成符合前端 echarts 输入的格式
	// echarts 中 name 必须不同，因此给 name 相同的实体加上 -0 -1这样的后缀来做区分
	for _, tuple := range tuples {
		id2nodePtr[tuple.FirstNode.Id] = tuple.FirstNode
		id2nodePtr[tuple.SecondNode.Id] = tuple.SecondNode
		removeDuplicateNodeName(duplicateNodeNameToLeftTime, tuple.FirstNode)
		removeDuplicateNodeName(duplicateNodeNameToLeftTime, tuple.SecondNode)
	}

	echartNodes := make([]EchartsNode, len(id2nodePtr))
	echartLinks := make([]EchartsLink, len(tuples))

	// make data
	count := 0
	for _, nodePtr := range id2nodePtr {
		enodePtr := makeEchartNode(nodePtr)
		echartNodes[count] = *enodePtr
		count++
	}

	// make links
	for index, tuple := range tuples {
		relationshipPtr := tuple.Relationship
		elinkPtr := makeEchartLink(relationshipPtr, id2nodePtr)
		echartLinks[index] = *elinkPtr
	}

	echartReturn := EchartsReturn{
		Data:  echartNodes,
		Links: echartLinks,
	}

	return &echartReturn
}

func QueryNode(c *gin.Context) {
	text := c.Query("text")
	limit, err := strconv.Atoi(c.Query("limit"))
	if err != nil {
		limit = 50
	}

	tuples, err := database.SearchByEventName(text, limit)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"code": 4001, "data": "内部数据库查询出错"})
		return
	}
	echartReturn := makeEchartReturn(tuples)

	c.JSON(http.StatusOK, gin.H{
		"code": 200,
		"data": &echartReturn,
	})
}

func QueryRelation(c *gin.Context) {
	startText := c.Query("start")
	endText := c.Query("end")
	limit, err := strconv.Atoi(c.Query("limit"))
	if err != nil {
		limit = 50
	}

	tuples, err := database.SearchByTwoEventNames(startText, endText, limit)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"code": 4001, "data": "内部数据库查询出错"})
		return
	}
	echartReturn := makeEchartReturn(tuples)

	c.JSON(http.StatusOK, gin.H{
		"code": 200,
		"data": &echartReturn,
	})
}
